# -*- coding: utf-8 -*-
# @File: node.py
# @Author: Liccsu
# @Time: 8/11/2022 上午12:51
# @Project: AI-01-AStar


import numpy as np


class Node:
    """
    self.node: 当前节点的数码矩阵
    self._direction: 当前 0 的可移动方向
    self._parent: 当前节点的父节点
    self._f: 当前节点的估价函数值
    self._d: 当前节点的搜索深度
    """
    target_node = None

    def __init__(self, node, action="开始", direction_flag=None, parent=None, f=0, d=0):
        """
        :param node: 数码矩阵
        :param action: 移动方向
        :param direction_flag: 用于计算 0 点移动方向的标志位
        :param parent: 父节点
        :param f: 估价值
        :param d: 搜索深度
        """
        self._node = node
        self._action = action
        self._direction = ['上', '下', '左', '右']
        if direction_flag:
            self._direction.remove(direction_flag)
        self._parent = parent
        self._f = f
        self._d = d

    def __lt__(self, other):
        """
        用于维护队列优先级
        :param other: 待比较节点
        :return: 当前节点 f 值是否小于待比较节点
        """
        return self.f < other.f

    @property
    def node(self):
        """
        :return: 当前节点的矩阵
        """
        return self._node

    @property
    def action(self):
        """
        :return: 当前节点移动方向
        """
        return self._action

    @property
    def direction(self):
        """
        :return: 当前节点下一步可移动的方向
        """
        return self._direction

    @property
    def parent(self):
        """
        :return: 当前节点父节点
        """
        return self._parent

    @property
    def f(self):
        """
        :return: 估价函数值
        """
        return self._f

    @property
    def d(self):
        """
        :return: 搜索深度
        """
        return self._d

    def set_f(self, f):
        """
        :param f: 设置 f 值
        """
        self._f = f

    def print_step(self):
        """
        打印此节点
        """
        print(self.action)
        print(self.node)

    def inverse_number(self):
        """
        :return: 当前节点逆序数
        """
        _sum = 0
        _ls = [x for item in self.node for x in item]
        for _i in range(0, len(_ls)):
            if _ls[_i] == 0:
                continue
            for _j in range(0, _i):
                if _ls[_j] > _ls[_i]:
                    _sum += 1
        return _sum

    def get_zero_pos(self):
        """
        :return: 0 在数码矩阵中的位置
        """
        position = np.where(self.node == 0)
        return position

    def get_function_value(self):
        """
        :return: 当前节点到目标节点的曼哈顿距离 + 搜索深度
        """
        cur_node = self.node.copy()
        fin_node = self.target_node.node.copy()
        dist = 0
        error = 0
        n = len(cur_node)
        for _i in range(n):
            for _j in range(n):
                if cur_node[_i][_j] != fin_node[_i][_j]:
                    index = np.argwhere(fin_node == cur_node[_i][_j])
                    x = index[0][0]
                    y = index[0][1]
                    dist += (abs(x - _i) + abs(y - _j))
                    error += 1
        return dist + self.d

    def sub_nodes(self):
        """
        :return: 当前节点的子节点的集合
        """
        if not self.direction:
            return []
        sub_nodes_list = []
        # 边界
        boarder = len(self.node) - 1
        # 获取0点位置
        x, y = self.get_zero_pos()
        # 向上
        if '上' in self.direction and x > 0:
            s = self.node.copy()
            tmp = s[x - 1, y]
            s[x - 1, y] = s[x, y]
            s[x, y] = tmp
            news = Node(s, action="向上", direction_flag='下', parent=self, d=self.d + 1)
            news.set_f(news.get_function_value())
            sub_nodes_list.append(news)
        # 向下
        if '下' in self.direction and x < boarder:
            s = self.node.copy()
            tmp = s[x + 1, y]
            s[x + 1, y] = s[x, y]
            s[x, y] = tmp
            news = Node(s, action="向下", direction_flag='上', parent=self, d=self.d + 1)
            news.set_f(news.get_function_value())
            sub_nodes_list.append(news)
        # 向左
        if '左' in self.direction and y > 0:
            s = self.node.copy()
            tmp = s[x, y - 1]
            s[x, y - 1] = s[x, y]
            s[x, y] = tmp
            news = Node(s, action="向左", direction_flag='右', parent=self, d=self.d + 1)
            news.set_f(news.get_function_value())
            sub_nodes_list.append(news)
        # 向右
        if '右' in self.direction and y < boarder:
            s = self.node.copy()
            tmp = s[x, y + 1]
            s[x, y + 1] = s[x, y]
            s[x, y] = tmp
            news = Node(s, action="向右", direction_flag='左', parent=self, d=self.d + 1)
            news.set_f(news.get_function_value())
            sub_nodes_list.append(news)
        # 所有状态
        return sub_nodes_list
